package model.wrap.node;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import security.Security;

import model.entityes.exception.ByteContentException;
import model.entityes.exception.FloatContentException;
import model.entityes.exception.PointContentException;
import model.entityes.exception.StringContentException;
import model.entityes.exception.UserContentException;
import model.forview.NodeCreateModelForView;
import model.wrap.content.Content;
import model.wrap.group.UserGroup;
import model.wrap.type.StringElement;
import model.wrap.type.Type;
import model.wrap.user.User;
import model.wrap.user.UserException;
import models.ByteContent;
import models.FloatContent;
import models.Link;
import models.PointContent;
import models.StringContent;
import models.Structure;
import models.UserContent;

/**
 * 
 * @author maslov-r
 * 
 */
public class Node
{
    protected Structure node;

    protected ArrayList<Structure> parents;
    protected ArrayList<Structure> child;

    protected model.wrap.structure.Structure structure;

    protected Content content;

    protected ArrayList<UserGroup> writeAccessGroup;

    public Node(NodeCreateModelForView forModel)
    {
	node = new Structure();
	node.setId_type(forModel.typeId);
	node.setId_structure(forModel.structureId);
	node.setTitle(forModel.title);
    }

    public ArrayList<Node> getParents()
    {
	return Link.getParents(this);
    }

    public ArrayList<Node> getChildren()
    {
	return Link.getChild(this);
    }

    public Node(Long id)
    {
	this.node = Structure.getInstanceById(id);
	this.content = new Content(this);
    }

    public Node(Structure str)
    {
	this.node = str;
	this.content = new Content(this);
    }

    public void save()
    {
	node.setCreate_date(String.valueOf(System.currentTimeMillis()));
	// TODO установить значение пользователя
	node.setCreater(Security.getUserNameFromSession());
	node.setLastchange_date(String.valueOf(System.currentTimeMillis()));
	// TODO установить значение последнего изменяющего
	node.setChanger(Security.getUserNameFromSession());
	node.save();
    }

    public void update()
    {
	node.setLastchange_date(String.valueOf(System.currentTimeMillis()));
	// TODO установить значение последнего изменяющего
	node.setChanger(Security.getUserNameFromSession());
	node.update();
    }

    public void delete()
    {
	// Delete all links
	ArrayList<Link> links = Link.getAllLinks(this);
	for (Link link : links)
	{
	    link.delete();
	}
	// Delete all content
	ArrayList<String> contentsId = this.getContentsId();
	ArrayList<String> contentsType = this.getContentsTypeName();

	for (int i = 0; i < contentsId.size(); i++)
	{
	    try
	    {
		Long id = Long.valueOf(contentsId.get(i));
		if (contentsType.get(i).equals(Type.BYTE)) ByteContent.getById(id).delete();
		if (contentsType.get(i).equals(Type.FLOAT)) FloatContent.getById(id).delete();
		if (contentsType.get(i).equals(Type.POINT)) PointContent.getById(id).delete();
		if (contentsType.get(i).equals(Type.STRING))StringContent.getById(id).delete();
		if (contentsType.get(i).equals(Type.USER))UserContent.getById(id).delete();
	    }
	    catch (NumberFormatException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    catch (ByteContentException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    catch (FloatContentException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    catch (PointContentException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    catch (StringContentException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	    catch (UserContentException e)
	    {
		// TODO Auto-generated catch block
		e.printStackTrace();
	    }
	}
	// Delete node
	this.node.delete();
    }

    public static Node createDraft(NodeCreateModelForView form)
    {
	Node node = new Node(form);
	node.setIsDraft(true);
	node.save();

	node.setContent(Type.createDefaultValuesForNode(node));

	node.update();

	return node;
    }

    public static ArrayList<Node> getAllUsersNodes(User user)
    {
	List<Structure> listOfNodes = Structure.getByCreater(user.getEmail());
	ArrayList<Node> result = new ArrayList<Node>();
	for (Structure structure : listOfNodes)
	{
	    result.add(new Node(structure));
	}
	return result;
    }
    
    public static ArrayList<Node> getAllDraftsNodes()
    {
	List<Structure> listOfNodes = Structure.getAllDrafts();
	ArrayList<Node> result = new ArrayList<Node>();
	for (Structure structure : listOfNodes)
	{
	    result.add(new Node(structure));
	}
	return result;
    }
    
    public static ArrayList<Node> getNodesByType(Type type)
    {
	List<Structure> listOfNodes = Structure.getByType(type.getID());
	ArrayList<Node> result = new ArrayList<Node>();
	for (Structure structure : listOfNodes)
	{
	    result.add(new Node(structure));
	}
	return result;
    }
    
    public static ArrayList<Node> search(String query)
    {
	if(null == query || query.equals(""))
	    return new ArrayList<Node>();
	
	Set<Long> setContent = StringElement.searchNodesId(query);
	List<Structure> listStr = Structure.getByTitle(query);
	ArrayList<Node> result = new ArrayList<Node>();
	for (Structure structure : listStr)
	{
	    setContent.add(structure.getId_node());
	}
	Iterator<Long> iterator = setContent.iterator(); 
	while (iterator.hasNext())
	{
	    result.add(new Node(iterator.next()));
	}
	return result;
    }
    
    public Long getId()
    {
	return node.getId_node();
    }

    public int getSizeOfParents()
    {
	return 0;
    }

    public long getParentIdByIndex(int index) throws NodeException
    {
	return 0;
    }

    public User getCreator()
    {// TODO
	try
	{
	    return new User(this.node.getCreater());
	}
	catch (UserException e)
	{
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	return null;
    }

    public void setCreator(User creator) throws NodeException
    {
	// проверить, если создатель существует, значит изменить нельзя
    }

    public User getLastChangeUser()
    {
	return null;
    }

    public void setLastChangeUser(User user) throws NodeException
    {

    }

    public int getSizeAllowToWriteGroup()
    {
	return 0;
    }

    public UserGroup getAllowToWriteGroup(int index) throws NodeException
    {
	return null;
    }

    public void addAllowToWriteGroup(UserGroup allowGroup) throws NodeException
    {

    }

    public void deleteAllowToWriteGroup(int index) throws NodeException
    {

    }

    public Content getContent()
    {
	return new Content(this);
    }

    public void setContent(String content)
    {
	this.node.setIds_content(content);
    }

    public Type getType()
    {
	return new Type(node.getId_type());
    }

    public model.wrap.structure.Structure getStructure()
    {
	return new model.wrap.structure.Structure(node.getId_structure());
    }

    public String getTitle()
    {
	return this.node.getTitle();
    }

    public String idDraftToString()
    {
	if(this.node.isIs_draft())
	    return "Да";
	return "Нет";
    }
    
    public void setTitle(String title)
    {
	this.node.setTitle(title);
    }

    public void setIsDraft(boolean isDraft)
    {
	this.node.setIs_draft(isDraft);
    }

    public boolean isDraft()
    {
	return this.node.isIs_draft();
    }

    public String getCreateDate()
    {
	return this.node.getCreate_date();
    }

    public String getChangeDate()
    {
	return this.node.getLastchange_date();
    }
    
    public String getChangeFormatDate()
    {
	Date date = new Date(Long.valueOf(getChangeDate()));
	return new SimpleDateFormat("dd-MM-yyyy").format(date); 
    }
    
    public String getCreateFormatDate()
    {
	Date date = new Date(Long.valueOf(getCreateDate()));
	return new SimpleDateFormat("dd-MM-yyyy").format(date); 
    }
    
    public User getCreater()
    {
	try
	{
	    return new User(this.node.getCreater());
	}
	catch (UserException e)
	{
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	return null;
    }

    public User getChanger()
    {
	try
	{
	    return new User(this.node.getChanger());
	}
	catch (UserException e)
	{
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	return null;
    }

    public ArrayList<String> getContentsId()
    {
	ArrayList<String> contentsId = new ArrayList<String>();

	if(null == this.node.getIds_content()) return contentsId;
	String[] contentsIdFromeNode = this.node.getIds_content().split(Node.SEPARATOR_FOR_CONTENT);
	for (String string : contentsIdFromeNode)
	{
	    contentsId.add(string);
	}

	return contentsId;
    }

    public ArrayList<String> getContentsTypeName()
    {
	return this.getType().getSimpleTypesName();
    }

    public ArrayList<String> getContentsTypeTitle()
    {
	return this.getType().getSimpleTypesTitle();
    }

    protected Node(User creater, model.wrap.structure.Structure structure) throws NodeException
    {

    }

    /**
     * @author pocteg
     * @param str
     * @return
     */
    public static ArrayList<Node> getAllNodes(model.wrap.structure.Structure str)
    {
	List<Structure> allStrList = Structure.getAll(str.getId());
	ArrayList<Node> allNodes = new ArrayList<Node>();

	for (Structure structure : allStrList)
	{
	    allNodes.add(new Node(structure));
	}

	return allNodes;
    }

    
    /**
     * Add(to db) parent to node.
     * 
     * @param parent
     */
    public void addParent(Node parent)
    {
	ArrayList<Node> check = Link.getParents(this);
	for (Node node : check)
	{
	    if (node.getId().equals(parent.getId())) return;
	}

	Link link = new Link();

	link.setId_child(this.getId());
	link.setId_parent(parent.getId());
	link.setId_structure(this.getStructure().getId());

	link.save();
    }

    public void addChild(Node child)
    {
	ArrayList<Node> check = Link.getChild(this);
	for (Node node : check)
	{
	    if (node.getId().equals(child.getId())) return;
	}
	//if (this.getStructure().getId() != child.getStructure().getId()) return;

	Link link = new Link();

	link.setId_child(child.getId());
	link.setId_parent(this.getId());
	link.setId_structure(this.getStructure().getId());

	link.save();
    }

    /**
     * @author pocteg
     * @param str
     * @return
     */
    public static ArrayList<Node> getRootNodes(model.wrap.structure.Structure str)
    {
	ArrayList<Node> root = new ArrayList<Node>();
	ArrayList<Node> allNode = Node.getAllNodes(str);

	for (Node node : allNode)
	{
	    ArrayList<Node> tmp = Link.getParents(node);
	    if (tmp.size() == 0)
	    {
		root.add(node);
	    }
	}

	return root;
    }

    public final static String SEPARATOR_FOR_CONTENT = ";";
}
